package com.shenzw.common.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.shenzw.common.aop.anno.ResponseResult;
import com.shenzw.common.config.pojo.SuccessInfo;
import lombok.AllArgsConstructor;
import lombok.NonNull;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.lang.reflect.Method;
import java.util.Objects;

/**
 * 拦截器【统一处理消息返回体】
 * <p/>
 * 这只是成功的处理，也即业务出错了并不会走到这里进行统一的错误处理
 *
 * @author shenzw
 * @date 2023/10/29
 */
@Slf4j
@ControllerAdvice
@AllArgsConstructor
public class ResponseResultHandler implements ResponseBodyAdvice<Object> {
    /**
     * supports 方法，主要是判断方法或者类是否标注了我们自定义的统一结果处理的注解，有则表明需要做统一结果处理，进入到后续的
     * beforeBodyWrite 方法做具体处理，没有则表示不需要做处理，即不会进入 beforeBodyWrite
     *
     * @param returnType    方法参数
     * @param converterType HttpMessageConverter
     * @return 是否进入 beforeBodyWrite 方法【true.进入；false.不进入】
     */
    @Override
    public boolean supports(@NonNull MethodParameter returnType, @NonNull Class<? extends HttpMessageConverter<?>> converterType) {

        // 获取方法和 class 对象
        Method method = returnType.getMethod();
        Class<?> clazz = Objects.requireNonNull(method, "method is null！").getDeclaringClass();

        // 只处理标志有注解 ResponseResult 标注的类或方法
        ResponseResult annotation = clazz.getAnnotation(ResponseResult.class);
        if (annotation == null) {
            annotation = method.getAnnotation(ResponseResult.class);
        }

        // 如果是 FileSystemResource 文件类型则不进行拦截，放行
        if (FileSystemResource.class.getTypeName()
                .equals(method.getAnnotatedReturnType().getType().getTypeName())) {
            return false;
        }

        // 注解不为空，并且是不忽略，走 beforeBodyWrite 方法处理返回结果
        return annotation != null && !annotation.ignore();
    }

    /**
     * 将方法返回的结果统一包装到定义好的 SuccessInfo 对象中，然后返回结果处理就 ok 了，这样经过统一处理，
     * 所有成功的方法都会在结果到达页面之前进行这一步的处理，从而达到我们需要的效果
     *
     * @param body                  T
     * @param returnType            MethodParameter
     * @param mediaType             MediaType
     * @param selectedConverterType HttpMessageConverter
     * @param serverHttpRequest     ServerHttpRequest
     * @param serverHttpResponse    ServerHttpResponse
     * @return T
     */
    @SneakyThrows
    @Override
    public Object beforeBodyWrite(Object body, @NonNull MethodParameter returnType,
                                  @NonNull MediaType mediaType,
                                  @NonNull Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  @NonNull ServerHttpRequest serverHttpRequest,
                                  @NonNull ServerHttpResponse serverHttpResponse) {

        SuccessInfo<Object> successInfo = SuccessInfo.builder()
                .data(body)
                .build();

        // 处理 String 类型情况
        if ((body instanceof String) && !MediaType.APPLICATION_XML_VALUE.equals(mediaType.toString())) {
            ObjectMapper om = new ObjectMapper();
            serverHttpResponse.getHeaders().set("Content-Type", "application/json");
            return om.writeValueAsString(successInfo);
        }

        // 处理 void 类型情况
        if (Objects.isNull(body) && MediaType.TEXT_HTML_VALUE.equals(mediaType.toString())) {
            ObjectMapper om = new ObjectMapper();
            serverHttpResponse.getHeaders().set("Content-Type", "application/json");
            return om.writeValueAsString(successInfo);
        }

        return successInfo;
    }
}
